#pragma once

#include <cstdint>
#include <string>
#include <sys/cdefs.h>
#include <vector>
#include "XnccType.h"
#include "nlohmann/json.hpp"
using nlohmann::json;

typedef int64_t user_id_t;

struct User
{
    user_id_t                    id;
    std::string                  name;
    std::string                  password;
    std::string                  ip;
    xncc::types::connection_id_t sessionId;
    xncc::types::thread_id_t     tid;
    bool                         isOnline = false;
    std::vector<user_id_t>       friends;

    std::string toJson() const
    {
        json j;
        j["id"]        = id;
        j["name"]      = name;
        j["password"]  = password;
        j["ip"]        = ip;
        j["sessionId"] = sessionId;
        j["tid"]       = tid;
        j["isOnline"]  = isOnline;
        j["friends"]   = friends;
        return j.dump();
    }
};

void from_json(const json& j, User& p);

constexpr xncc::types::message_id_t USER_LOGIN_REQ      = {0x1000};
constexpr xncc::types::message_id_t USER_LOGIN_RES      = {0x1001};
constexpr xncc::types::message_id_t USER_LOGOUT_REQ     = {0x1002};
constexpr xncc::types::message_id_t USER_LOGOUT_RES     = {0x1003};
constexpr xncc::types::message_id_t USER_LIST_REQ       = {0x1004};
constexpr xncc::types::message_id_t USER_LIST_RES       = {0x1005};
constexpr xncc::types::message_id_t USER_CHAT_MSG       = {0x1006};
constexpr xncc::types::message_id_t USER_CHAT_RES       = {0x1007};
constexpr xncc::types::message_id_t USER_ONLINE_OFFLINE = {0x1008};
constexpr xncc::types::message_id_t USER_REGISTER_REQ   = {0x1009};
constexpr xncc::types::message_id_t USER_REGISTER_RES   = {0x100a};
constexpr xncc::types::message_id_t ADD_DEL_FRIEND_REQ  = {0x100b};
constexpr xncc::types::message_id_t ADD_DEL_FRIEND_RES  = {0x100c};

struct UserRegisterReq
{
    std::string name;
    std::string password;

    std::string toJson() const
    {
        json j;
        j["name"]     = name;
        j["password"] = password;
        return j.dump();
    }
    static UserRegisterReq fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return UserRegisterReq{j["name"], j["password"]};
    }
};

TYPE_MESSAGE_ID_BIND(UserRegisterReq, USER_REGISTER_REQ)

struct UserRegisterRes
{
    user_id_t                    id;
    std::string                  name;
    xncc::types::connection_id_t sessionId;
    std::string                  result;

    std::string toJson() const
    {
        json j;
        j["id"]         = id;
        j["name"]       = name;
        j["session_id"] = sessionId;
        j["result"]     = result;
        return j.dump();
    }

    static UserRegisterRes fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return UserRegisterRes{j["id"], j["name"], j["session_id"], j["result"]};
    }
};

TYPE_MESSAGE_ID_BIND(UserRegisterRes, USER_REGISTER_RES)

struct UserLoginReq
{
    user_id_t           id;
    std::string         password;
    static UserLoginReq fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return UserLoginReq{j["id"], j["password"]};
    }

    std::string toJson() const
    {
        json j;
        j["id"]       = id;
        j["password"] = password;
        return j.dump();
    }
};
TYPE_MESSAGE_ID_BIND(UserLoginReq, USER_LOGIN_REQ)

struct UserLoginRes
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    std::string                  name;
    std::string                  loginResult;

    std::string toJson() const
    {
        json j;
        j["id"]         = id;
        j["name"]       = name;
        j["session_id"] = sessionId;
        j["result"]     = loginResult;
        return j.dump();
    }
};

TYPE_MESSAGE_ID_BIND(UserLoginRes, USER_LOGIN_RES)

struct UserLogoutReq
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    static UserLogoutReq         fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return UserLogoutReq{j["id"], j["session_id"]};
    }
};

TYPE_MESSAGE_ID_BIND(UserLogoutReq, USER_LOGOUT_REQ)

struct UserLogoutRes
{
    user_id_t   id;
    std::string logoutResult;
    std::string toJson() const
    {
        json j;
        j["id"]     = id;
        j["result"] = logoutResult;
        return j.dump();
    }
};

TYPE_MESSAGE_ID_BIND(UserLogoutRes, USER_LOGOUT_RES)

struct UserListReq
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    bool                         reqAllUsers;

    static UserListReq fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return UserListReq{j["id"], j["session_id"], j["req_all_users"]};
    }

    std::string toJson() const
    {
        json j;
        j["id"]            = id;
        j["session_id"]    = sessionId;
        j["req_all_users"] = reqAllUsers;
        return j.dump();
    }
};

TYPE_MESSAGE_ID_BIND(UserListReq, USER_LIST_REQ)

struct simpleUser
{
    user_id_t   id;
    std::string name;
    bool        isOnline;
};

struct UserListRes
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    bool                         reqAllUsers;
    std::vector<simpleUser>      friendUsers;
    std::string                  result;
    std::string                  toJson() const
    {
        json j;
        j["id"]            = id;
        j["session_id"]    = sessionId;
        j["req_all_users"] = reqAllUsers;

        for (const auto& user : friendUsers) {
            auto userJson      = json::object();
            userJson["id"]     = user.id;
            userJson["name"]   = user.name;
            userJson["online"] = user.isOnline;
            j["friend_users"].push_back(userJson);
        }
        j["result"] = result;

        return j.dump();
    }
};

TYPE_MESSAGE_ID_BIND(UserListRes, USER_LIST_RES)

struct UserChatMsg
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    user_id_t                    toId;
    std::string                  msg;

    std::string toJson() const
    {
        json j;
        j["id"]         = id;
        j["session_id"] = sessionId;
        j["to_id"]      = toId;
        j["msg"]        = msg;
        return j.dump();
    }

    static UserChatMsg fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return UserChatMsg{j["id"], j["session_id"], j["to_id"], j["msg"]};
    }
};

TYPE_MESSAGE_ID_BIND(UserChatMsg, USER_CHAT_MSG)

struct UserChatRes
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    user_id_t                    toId;
    std::string                  result;

    std::string toJson() const
    {
        json j;
        j["id"]         = id;
        j["session_id"] = sessionId;
        j["to_id"]      = toId;
        j["result"]     = result;
        return j.dump();
    }
};

TYPE_MESSAGE_ID_BIND(UserChatRes, USER_CHAT_RES)

struct UserOnOffline
{
    user_id_t   id;
    std::string name;
    bool        online;

    std::string toJson() const
    {
        json j;
        j["id"]     = id;
        j["name"]   = name;
        j["online"] = online;
        return j.dump();
    }
};

TYPE_MESSAGE_ID_BIND(UserOnOffline, USER_ONLINE_OFFLINE)

struct AddDelFriendReq
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    user_id_t                    friendId;
    bool                         add;

    std::string toJson() const
    {
        json j;
        j["id"]         = id;
        j["session_id"] = sessionId;
        j["friend_id"]  = friendId;
        j["add"]        = add;
        return j.dump();
    }

    static AddDelFriendReq fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return AddDelFriendReq{j["id"], j["session_id"], j["friend_id"], j["add"]};
    }
};

TYPE_MESSAGE_ID_BIND(AddDelFriendReq, ADD_DEL_FRIEND_REQ)

struct AddDelFriendRes
{
    user_id_t                    id;
    xncc::types::connection_id_t sessionId;
    user_id_t                    friendId;
    bool                         add;
    std::string                  result;

    std::string toJson() const
    {
        json j;
        j["id"]         = id;
        j["session_id"] = sessionId;
        j["friend_id"]  = friendId;
        j["add"]        = add;
        j["result"]     = result;
        return j.dump();
    }

    static AddDelFriendRes fromJson(const std::string& jsonStr)
    {
        json j = json::parse(jsonStr);
        return AddDelFriendRes{j["id"], j["session_id"], j["friend_id"], j["add"], j["result"]};
    }
};

TYPE_MESSAGE_ID_BIND(AddDelFriendRes, ADD_DEL_FRIEND_RES)
